// Copyright (C) joyinter
// All rights reserved.
// 
// Author : Jiang Li
// Date   : 2004-11-8

#include "stdafx.h"
#include "net_accept.h"

namespace JoyServer
{

CJoyAcceptOverlapped::CJoyAcceptOverlapped(CJoySockData* sockData, SOCKET acceptSocket):
    CJoyOverlapped(sockData), m_acceptSocket(acceptSocket)
{
}

CJoyAcceptOverlapped::~CJoyAcceptOverlapped()
{
    if(m_acceptSocket != INVALID_SOCKET)
    {
        ::shutdown(m_acceptSocket, SD_BOTH);
        ::closesocket(m_acceptSocket);
    }
}

void CJoyAcceptOverlapped::OnComletion(DWORD dwTransferred, DWORD err)
{
    dwTransferred;

    CJoyAutoCS gate(m_sockData->m_ownerLock);
    CJoyAcceptorEx* pOwner = (CJoyAcceptorEx*)m_sockData->m_owner;
    if(pOwner == NULL)
    {
        return;
    }

    if(err != 0)
    {
        if(err == ERROR_OPERATION_ABORTED)
        {
            return;
        }
        pOwner->OnError((int)err);
        int accErr = 0;
        pOwner->QueueAccept(accErr);
        return;
    }

    // socket m_acceptSocket does not inherit the properties of the
    // socket associated with m_sockData->m_handle until
    // SO_UPDATE_ACCEPT_CONTEXT is set on the socket. 
    setsockopt(m_acceptSocket,
        SOL_SOCKET, 
        SO_UPDATE_ACCEPT_CONTEXT, 
        (char *)&m_sockData->m_handle, 
        sizeof(m_sockData->m_handle) );

    // parse the data obtained from a call to the AcceptEx function
    // and passes the local and remote addresses to a sockaddr structure.
    // this function is required, because the AcceptEx function writes
    // address information in an internal (TDI) format.
    sockaddr* localAddr;
    sockaddr* remoteAddr;
    int localLen = sizeof(sockaddr_in), remoteLen = sizeof(sockaddr_in);
    pOwner->m_pfnGetAcceptExAddrs(
        m_outputBuffer, 
        0,
        sizeof(sockaddr_in)+16,
        sizeof(sockaddr_in)+16,
        &localAddr,
        &localLen,
        &remoteAddr,
        &remoteLen);

    if(pOwner->OnAccept(m_acceptSocket, *(sockaddr_in*)remoteAddr))
    {
        m_acceptSocket = INVALID_SOCKET;
    }

    int accErr = 0;
    pOwner->QueueAccept(accErr);
}

// class CJoyAcceptorEx

CJoyAcceptorEx::CJoyAcceptorEx()
{
    m_pfnAcceptEx = NULL;
    m_pfnGetAcceptExAddrs   = NULL;
}

CJoyAcceptorEx::~CJoyAcceptorEx()
{
}

bool CJoyAcceptorEx::OnAccept(SOCKET s, const sockaddr_in& addr)
{
    s;addr;
    return false;
}

bool CJoyAcceptorEx::Attach(SOCKET s, bool bStream, int& err)
{
    err = 0;
    if(!bStream)
    {
        return false;
    }

    // do not call AcceptEx directly.
    // used funtion pointer instead.
    // this is for performance.

    static GUID acceptExId = WSAID_ACCEPTEX;
    static GUID getAcceptExAddrs = WSAID_GETACCEPTEXSOCKADDRS;

    DWORD dwResult = 0;
    int nResult = 0;
	nResult = WSAIoctl( 
        s,
		SIO_GET_EXTENSION_FUNCTION_POINTER,
		&acceptExId,
		sizeof(acceptExId),
		&m_pfnAcceptEx,
		sizeof(m_pfnAcceptEx),
		&dwResult,
		NULL,
		NULL );

    if(nResult == SOCKET_ERROR)
    {
        err = WSAGetLastError();
        return false;
    }

	nResult = WSAIoctl( 
        s,
		SIO_GET_EXTENSION_FUNCTION_POINTER,
		&getAcceptExAddrs,
		sizeof(getAcceptExAddrs),
		&m_pfnGetAcceptExAddrs,
		sizeof(m_pfnGetAcceptExAddrs),
		&dwResult,
		NULL,
		NULL );

    if(nResult == SOCKET_ERROR)
    {
        err = WSAGetLastError();
        return false;
    }

    return __super::Attach(s, bStream, err);
}

bool CJoyAcceptorEx::Start(int acceptCount, int& err)
{
    if(m_sockData == NULL)
    {
        return false;
    }

    if(SOCKET_ERROR == listen(m_sockData->m_handle, SOMAXCONN))
    {
        err = WSAGetLastError();
        return false;
    }

    for(int i = 0; i < acceptCount; ++ i)
    {
        if(!QueueAccept(err))
        {
            return false;
        }
    }
    return true;
}

bool CJoyAcceptorEx::Start(int acceptCount)
{
    int err = 0;
    return Start(acceptCount, err);
}

bool CJoyAcceptorEx::QueueAccept(int& err)
{
    if(m_sockData == NULL)
    {
        return false;
    }
    DWORD recvd = 0;
    SOCKET acceptSocket = CreateSocket(true);
    if(acceptSocket == SOCKET_ERROR)
    {
        err = WSAGetLastError();
        return false;
    }

    CJoyAcceptOverlapped* pOverlapped = 
        new CJoyAcceptOverlapped(m_sockData, acceptSocket);

    // here we post asynchronous Accept operation.
    BOOL b = m_pfnAcceptEx(m_sockData->m_handle, acceptSocket, pOverlapped->m_outputBuffer, 0,
        sizeof(sockaddr_in)+16, sizeof(sockaddr_in)+16, &recvd, pOverlapped->ToOsOverlapped());
    if(!b)
    {
        int e = WSAGetLastError();
        if(e != WSA_IO_PENDING)
        {
            err = e;
            delete pOverlapped;
            return false;
        }
    }

    return true;
}

bool CJoyAcceptorEx::QueueAccept()
{
    int err = 0;
    return QueueAccept(err);
}

} // namespace JoyServer